/*!
 * Copyright 2012, Kapil Kashyap
 * 
 * Date: Wed Jul 18 2012 20:14:30 GMT+0530 (India Standard Time)
 */
;(function($) {
	$.widget( "pramati.suggestions", {
	    options: {
	    	url: null,
			template: null,
			elementValueProperty: null,
			itemsPerPage: 10,
			dropdownWidth: null,
			elementType: 'text',
			persistState: true,
			closeBtnRequired: true,
			footerRequired: true,
			keyNavigation: {
				updown: true,
				leftright: true
			},
			triggerOn: null,
			cache: true
	    },
	    _create: function() {
	    	var wrapper = null,
	    		ddw = 0;

	    	// set widget properties
	    	this._setWidgetProperties();
	    	// create a wrapper around the target element
	    	wrapper = $( "<div></div>" ).addClass( this.widgetBaseClass + this.hyphen + this.options.elementType + this.hyphen + this.wrapperLabel );

	    	this._setWidgetState();
	        this.element.addClass( this.widgetBaseClass );
	        // if dropdown width is specified use that, else calculate dropdown width dynamically.
	        if(this.options.dropdownWidth) {
	        	ddw = parseInt(this.options.dropdownWidth, 10);
	        	this.options.dropdownWidth = ddw ? ddw : 0;
	        }
        	this.element.wrap( wrapper.width(this.element.width() + (this.options.closeBtnRequired ? 16 : 4)));
        	if(this.options.triggerOn) {
//        		this.element.attr('unselectable', 'true')
//        					.attr('onselectstart', 'return false')
//			                .css('user-select', 'none');
        		
        		var overlay = $( "<div></div>" ).addClass( "x-textarea-overlay" )
        						.css("margin-top", -1 * (this.element.height() + ($.browser.webkit ? 2 : 0))),
        			hiddenInput = $( "<input></input>", {"type": "hidden", "value": "", "name": "x_textarea_input"} ).addClass( "x-textarea-input" );

        		this.element.parent().append(overlay[0]);
        		this.element.parent().append(hiddenInput[0]);
        	}
        	// bind the keyup or click event with the element to display the dropdown container.
	        this._bindEvents();
	        this._constructPaginationFooter();
	    },
	    _init: function(config) {
	    	config = config || this.options;
	    	$.ajaxSetup({cache: config.cache});
	    },
	    _setWidgetProperties: function() {
	    	this.widgetEventPrefix = "pdd";
	    	this.widgetBaseClass = "suggestions";
	    	this.selectedItemClass = "selected-item";
	    	this.space = " ";
	    	this.hyphen = "-";
	    	// labels
	    	this.ofLabel = "of";
	    	this.wrapperLabel = "wrapper";
	    	this.containerLabel = "container";
	    	this.contentLabel = "content";
	    	this.itemLabel = "item";
	    	this.footerLabel = "footer";
	    	this.footerContentLabel = "footer-content";
	    	this.footerInfoLabel = "footer-info";
	    	this.footerLeftNavLabel = "footer-left-nav";
	    	this.footerRightNavLabel = "footer-right-nav";
	    	this.closeBtnLabel = "close-button";
	    	// custom events
	    	this.itemSelectedEvent = "itemselected";
	    	this.matchRegEx = new RegExp("\\]\\w+");
			this.regEx = new RegExp("\\@\\[\\d+\\:", "g");
			this.reverseRegEx = new RegExp("\\:\\d+\\[\\@");
	    },
	    _bindEvents: function() {
	    	var self = this,
	    		el = self.element,
	    		textareaOverlay = el.parent().find( ".x-textarea-overlay" ),
	    		textareaHiddenInput = el.parent().find( ".x-textarea-input" );

    		if(self.options.triggerOn) {
    			
    			el.keydown(function( event ) {
    				console.log("keydown >>> " + event.keyCode, el.prop( "selectionStart" ), el.prop( "selectionEnd" ));
    				if(event.keyCode == 8) {
    					var textareaHiddenInputValue = textareaHiddenInput.val(),
							updatedTextareaValue = null,
							reverse = null,
							matchText = null,
							char = null;

						if(textareaHiddenInputValue != "") {
							char = textareaHiddenInputValue.charAt(textareaHiddenInputValue.length - 1);
							console.log(char);
							if(el.prop( "selectionEnd" ) - el.prop( "selectionStart" ) == 0) {
								reverse = textareaHiddenInputValue.split("").reverse().join("");
								matchText = reverse.substring(0, reverse.indexOf(" ") != -1 ? reverse.indexOf(" ") : reverse.indexOf("[@"));

								if(char == "]" && (matchText.search( self.matchRegEx ) == 0 || matchText.search( /\]\w+\:\d+/ ) == 0)) {
									if(reverse.indexOf(" ") != -1 && (reverse.indexOf(" ") < reverse.indexOf(":"))) {
										reverse = reverse.substring(2, reverse.indexOf(" ") + 1) + "]" + reverse.substring(reverse.indexOf(" ") + 1);
									}
									else {
										if(reverse.match(self.reverseRegEx)) {
											reverse = reverse.replace(self.reverseRegEx, "");
											reverse = reverse.substring(2);
										}
									}
									updatedTextareaValue = reverse.split("").reverse().join("");
									textareaHiddenInput.val( updatedTextareaValue );
									self._updateOverlay( updatedTextareaValue );
								}
								else {
									textareaHiddenInputValue = textareaHiddenInputValue.substring(0, (textareaHiddenInputValue.length - 1));
									textareaHiddenInput.val(textareaHiddenInputValue);
								}
							}
							else {
								alert("Selected text backspaced - Fix me!!!");
							}
						}
    				}
    				else if(event.keyCode == 46) {
    					alert("Delete pressed - Fix me!!!");
    				}
    			});
    			
    			el.keypress(function( event ) {
    				var char = null,
    					selectedItem = $( el.parent().find( "." + self.selectedItemClass ) ),
    					v = null;
    				
    				console.log(event.keyCode);
    				
    				if(event.keyCode == 13) {
        				event.preventDefault();
        				if(selectedItem.length > 0) {
        					if(self.options.elementValueProperty) {
        						v = self._getSelectedItemObject()[self.options.elementValueProperty];
        					} else {
        						v = selectedItem.text();
        					}
        					
        					if(!self.options.triggerOn) {
    	    					el.val( v ).focus();
    	    				}
    	    				else {
    	    					self._updateMarkup(self._getSelectedItemObject());
    	    				}
        					self._cleanup();
        					self._fireEvent(self.itemSelectedEvent, event);
        				}
        				return;
    				}
    				
    				else {
    					char = String.fromCharCode(event.keyCode);
    					if(char != "") {
    						textareaHiddenInput.val(textareaHiddenInput.val() ? textareaHiddenInput.val() + char : char);
    					}
    				}
    			});
    		}

    		el.keyup(function( event ) {
    			var selectedItem = $( el.parent().find( "." + self.selectedItemClass ) ),
    				newItemIndex = null,
    				highlightItemIndex = null,
    				_value = el.val();

    			// clean up the dropdown and return, if input element is empty or Esc key is pressed.
    			if(_value == "" || event.keyCode == 27) {
    				if(_value == "") {
    					self._resetWidgetState();
    				}
    				self._cleanup();
    				return;
    			}
    			else if(self.options.keyNavigation.updown && event.keyCode == 38) {
    				highlightItemIndex = self._getWidgetState().endIndex - self._getWidgetState().startIndex + 1;
    				newItemIndex = selectedItem.prev().index();

    				if(newItemIndex != -1) {
    					highlightItemIndex = newItemIndex + 1;
    				}
    				self._highlightItem( highlightItemIndex );
    				return;
    			}
    			else if(self.options.keyNavigation.updown && event.keyCode == 40) {
    				highlightItemIndex = 1;
    				newItemIndex = selectedItem.next().hasClass( self.widgetBaseClass + self.hyphen + self.footerLabel ) ? -1 : selectedItem.next().index();

    				if(newItemIndex != -1) {
    					highlightItemIndex = newItemIndex + 1;
    				}
    				self._highlightItem( highlightItemIndex );
    				return;
    			}
    			else if(self.options.keyNavigation.leftright && event.keyCode == 37) {
    				self._prevPage(event, self);
    				return;
    			}
    			else if(self.options.keyNavigation.leftright && event.keyCode == 39) {
    				self._nextPage(event, self);
    				return;
    			}
    			if(!self.options.triggerOn) {
    				self._fetchData( $.trim(_value) );
    			}
    			else {
    	    		if(_value.match( self.options.triggerOn )) {
    	    			var considerLength = el.prop( "selectionStart" ),
    	    				subString = _value.substring(0, considerLength),
    	    				splitArray = $( subString.split( self.options.triggerOn ) ),
    	    				last = splitArray.last();
    	    			
        				if(last.length > 0 && last[0] != "") {
        					self._fetchData(last[0]);
        				}
    	    		}
    	    		else {
    	    			self._cleanup();
    	    		}
    			}
    		});

			// remove the dropdown and clean up the dom.
			el.blur(function( event ) {
				setTimeout(function() {
					if(el.is(":focus")) {
						return false;
					}
					if(!self.options.persistState) {
						self._resetWidgetState();
					}
					self._cleanup();
				}, 200);
			});
	    },
	    _updateMarkup: function(o) {
	    	console.log(this._getWidgetState().query);
	    	var self = this,
	    		textarea = self.element,
	    		queryTemplate = this.options.triggerOn + self._getWidgetState().query,
	    		textareaHiddenInput = textarea.siblings( ".x-textarea-input" ),
	    		textareaValue = textarea.val(),
	    		lastIndexOfTextareaTrigger = textareaValue.indexOf( queryTemplate ),
		    	textareaHiddenInputValue = textareaHiddenInput.val(),
		    	lastIndexOfTextareaHiddenInputTrigger = textareaHiddenInputValue.indexOf( queryTemplate ),
		    	temp = null,
		    	updatedTextareaValue = "";

	    	temp = textareaValue.substring(lastIndexOfTextareaTrigger);
	    	textarea.val( textareaValue.replace(textareaValue.substring(lastIndexOfTextareaTrigger, lastIndexOfTextareaTrigger + (temp.indexOf(" ") != -1 ? temp.indexOf(" ") : temp.length)), o[self.options.elementValueProperty]) );
    		// hard coded id, need to replace with o["id"]
	    	temp = textareaHiddenInputValue.substring(lastIndexOfTextareaHiddenInputTrigger);
	    	updatedTextareaValue = textareaHiddenInputValue.replace(textareaHiddenInputValue.substring(lastIndexOfTextareaHiddenInputTrigger, lastIndexOfTextareaHiddenInputTrigger + (temp.indexOf(" ") != -1 ? temp.indexOf(" ") : temp.length)), "@[1234567890:" + o[self.options.elementValueProperty] + "]") ;
    		textareaHiddenInput.val( updatedTextareaValue );
    		// update textarea overlay to highlight text
    		self._updateOverlay( updatedTextareaValue );
	    },
	    _updateOverlay: function(updatedTextareaValue) {
	    	this.element.siblings( ".x-textarea-overlay" ).html( "" );
	    	this.element.siblings( ".x-textarea-overlay" ).html( updatedTextareaValue.replace(this.regEx, "<b>").replace(/]/g, "</b>") );
	    },
	    _nextPage: function(e, self) {
	    	var widgetState = self._getWidgetState();

	    	self.element.focus();
    		if(widgetState.page < widgetState.maximumNumberOfPages) {
    			widgetState.page++;
    			self._paginate();
    			self.element.parent().find( "." + self.widgetBaseClass + self.hyphen + self.containerLabel ).html( widgetState.html );
    			self._updateFooterInfo();
    			self._bindDropdownComponentEvents();
    			self._highlightItem(1);
    			self._markAllItems();
    		}
    	},
    	_prevPage: function(e, self) {
    		var widgetState = self._getWidgetState();

    		self.element.focus();
    		if(widgetState.page > 1) {
    			widgetState.page--;
    			self._paginate();
    			self.element.parent().find( "." + self.widgetBaseClass + self.hyphen + self.containerLabel ).html( widgetState.html );
    			self._updateFooterInfo();
    			self._bindDropdownComponentEvents();
    			self._highlightItem(1);
    			self._markAllItems();
    		}
    	},
	    _bindDropdownComponentEvents: function() {
	    	var self = this,
	    		parent = self.element.parent();

	    	// bind footer events only if footer is required.
	    	if(self.options.keyNavigation.footerRequired) {
	    		parent.find( "." + self.widgetBaseClass + self.hyphen + self.footerLabel ).bind("click dblclick", function( e ) {
	    			self.element.focus();
	    		});
	    	}

	    	// bind page navigation events only if it required.
	    	if(self.options.keyNavigation.leftright) {
	    		parent.find( "." + self.widgetBaseClass + self.hyphen + self.footerRightNavLabel ).bind("click dblclick", function( e ) {
	    			self._nextPage(e, self);
	    		});
	    		
	    		parent.find( "." + self.widgetBaseClass + self.hyphen + self.footerLeftNavLabel ).bind("click dblclick", function( e ) {
	    			self._prevPage(e, self);
	    		});
	    	}

	    	parent.find( "." + self.widgetBaseClass + self.hyphen + self.containerLabel + " > div" ).each(function(index, item) {
	    		if(!$( item ).hasClass( self.widgetBaseClass + self.hyphen + self.footerLabel )) {
	    			$( item ).click(function(e) {
	    				var v = "";
	    				self._highlightItem(index + 1);
	    				if(self.options.elementValueProperty) {
							v = self._getSelectedItemObject()[self.options.elementValueProperty];
						} else {
							v = $( this ).text();
						}
	    				
	    				// if triggerOn property is not specified, behave normally.
	    				if(!self.options.triggerOn) {
	    					self.element.val( v ).focus();
	    				}
	    				else {
	    					self._updateMarkup(self._getSelectedItemObject());
	    				}
						self._cleanup();

						self._fireEvent(self.itemSelectedEvent, e);
	    			});
	    		}
	    	});

	    	// fix for chrome.
	    	// this will focus the container in case of links so that on blur event fires.
	    	if(self.options.elementType && self.options.elementType == 'link') {
	    		parent.find( "." + self.widgetBaseClass ).focus();
	    	}
	    },
	    _fetchData: function(query) {
	    	var self = this;

	    	if(self.options.url) {
	    		$.getJSON(self.options.url, {"q": query}, function( data ) {
	    			// ----
	    			var config = {
							property: "title, name, email",
							value: query,
							checkContains: true,
							wrapper: true
						},
						filteredJson = $.fn.filterJson( data, config );

	    			self._getWidgetState().fetchedData = self._getWidgetState().data = filteredJson ? filteredJson : data;
	    			self._getWidgetState().query = query;
	    			// ----
	    			
//	    			self._getWidgetState().fetchedData = self._getWidgetState().data = data;
	    			self._constructDropDown();
	    		});
	    	}
	    },
	    _constructHtmlUsingTemplate: function() {
	    	var self = this,
	    		data = self._getWidgetState().data,
	    		constructedTemplate = [];

			if(!self.options.template || !data) {
				var errorMsg = "";
				if(!self.options.template) {
					errorMsg += "template is required.\n";
				}
				if(!data) {
					errorMsg += "data is required.";
				}

				if(window.console) {
					console.error ? console.error( errorMsg ) : console.log( errorMsg );
					return;
				}
				alert( errorMsg );
				return;
			}

			$( data ).each(function(index, item) {
				var templateClone = $( "<div></div>" ).html( self.options.template ).html();
				$.each(item, function(k, v) {
					var templateKey = "{@:" + k + "}",
						regEx = new RegExp(templateKey, "g");
					
					if(self.options.template.indexOf( templateKey ) != -1) {
						templateClone = templateClone.replace( regEx, v );
					}
				});
				constructedTemplate.push( templateClone );
			});
			return constructedTemplate;
		},
	    _constructDropDown: function() {
	    	var data = this._getWidgetState().data;

	    	this._cleanup();

			if(data == null || data.length == 0) {
				this._resetWidgetState();
				return;
			};

			if(this.options.template) {
				this._getWidgetState().data = this._constructHtmlUsingTemplate();
			}

			this._paginate();
			this._createDropdownComponents();
			this._updateFooterInfo();
			this._bindDropdownComponentEvents();

			this._highlightItem(this._getWidgetState().highlightedIndex);
			this._markAllItems();
	    },
	    _createDropdownComponents: function() {
	    	var self = this,
	    		pos = self.element.parent().position(),
	    		$dropdown_close_Btn = $("<div></div>", {
					"class": this.widgetBaseClass + this.hyphen + this.closeBtnLabel,
					"html": "<div class=\"btn\">x</div>",
					"click": function() {
	    				self._cleanup();
	    			}
				}),
				$dropdown_container = $("<div></div>", {
					"class": this.widgetBaseClass + this.hyphen + this.containerLabel,
					"width": this.options.dropdownWidth > 0 ? this.options.dropdownWidth : this.element.parent().width(),
					"html": this._getWidgetState().html
				});

	    	if(this.options.closeBtnRequired) {
	    		this.element.parent().append( $dropdown_close_Btn );
	    		$dropdown_close_Btn.position({
	    			my: "right",
	    			at: "right",
	    			of: this.element.parent(),
	    			offset: "-4 0"
	    		});
	    	}

			this.element.parent().append( $dropdown_container );
			$dropdown_container.position({
				my: "top",
				at: "bottom",
				of: this.element.parent()
			}).css("left", pos.left + "px");
	    },
		_constructPaginationFooter: function() {
			if(this.options.footerRequired) {
		    	var widgetState = this._getWidgetState();
	
		    	widgetState.paginationFooter = '<div class="' + this.widgetBaseClass + this.hyphen + this.footerLabel + '">';
					widgetState.paginationFooter += '<div class="' + this.widgetBaseClass + this.hyphen + this.footerContentLabel + '">';
						widgetState.paginationFooter += '<span class="' + this.widgetBaseClass + this.hyphen + this.footerLeftNavLabel + '">&lt;</span>';
						widgetState.paginationFooter += '<span class="' + this.widgetBaseClass + this.hyphen + this.footerInfoLabel + '"></span>';
						widgetState.paginationFooter += '<span class="' + this.widgetBaseClass + this.hyphen + this.footerRightNavLabel + '">&gt;</span>';
					widgetState.paginationFooter += '</div>';
				widgetState.paginationFooter += '</div>';
			}
		},
		_paginate: function() {
			var self = this,
				widgetState = self._getWidgetState(),
				ipp = self.options.itemsPerPage,
				threshold = null,
				paginatedItems = [];

			if(widgetState.data && widgetState.data.length) {
				// reset the page in case there is a change in the length of fetched results
				if(widgetState.data.length != widgetState.totalResults) {
					widgetState.page = 1;
					widgetState.highlightedIndex = 1;
				}
				widgetState.totalResults = widgetState.data.length;
			}

			widgetState.maximumNumberOfPages = Math.floor( widgetState.totalResults / ipp );
			if(widgetState.totalResults % ipp > 0) {
				widgetState.maximumNumberOfPages++;
			}

			if(widgetState.totalResults > 0) {
				threshold = ipp * widgetState.page;
				widgetState.startIndex = (threshold - ipp);
				for(var z = (threshold - ipp); z < threshold; z++) {
					if(z == widgetState.totalResults) {
						break;
					}
					paginatedItems.push(widgetState.data[z]);
				}
				widgetState.endIndex = --z;
			}

			if(widgetState.maximumNumberOfPages > 1) {
				paginatedItems.push( widgetState.paginationFooter );
			}
			widgetState.html = paginatedItems.join( "" );
		},
		_updateFooterInfo: function() {
			var widgetState = this._getWidgetState(), 
				_html = null;

			if(widgetState.maximumNumberOfPages > 1) {
				_html = (widgetState.startIndex + 1) + this.space + this.hyphen + this.space + (widgetState.endIndex + 1) + this.space + this.ofLabel + this.space + widgetState.totalResults;
				this.element.parent().find( "." + this.widgetBaseClass + this.hyphen + this.footerInfoLabel ).html( _html );
			}
		},
		_highlightItem: function(index) {
			// making sure that the already selected-item is removed first
			this._removeItemHighlight();
			this._getWidgetState().highlightedIndex = index;
			this.element.parent().find( "." + this.widgetBaseClass + this.hyphen + this.containerLabel + " > div:nth-child( " + index + " )" ).addClass( this.selectedItemClass );
		},
		_markAllItems: function() {
			var self = this,
				items = this.element.siblings( "." + this.widgetBaseClass + this.hyphen + this.containerLabel ).children();
			items.each(function(index, item) {
				if(!$( item ).hasClass( self.widgetBaseClass + self.hyphen + self.footerLabel )) {
					$( item ).addClass( self.widgetBaseClass + self.hyphen + self.itemLabel );
				}
			});
		},
		_removeItemHighlight: function(selectedItem) {
			if(!selectedItem) {
				selectedItem = this.element.parent().find("." + this.widgetBaseClass + this.hyphen + this.containerLabel + " > div." + this.selectedItemClass);
			}
			selectedItem.removeClass( this.selectedItemClass );
		},
		_cleanup: function() {
			if(this.options.closeBtnRequired) {
				this.element.parent().find( "." + this.widgetBaseClass + this.hyphen + this.closeBtnLabel ).remove();
			}
			this.element.parent().find( "." + this.widgetBaseClass + this.hyphen + this.containerLabel ).remove();
		},
		_resetWidgetState: function() {
			this._setWidgetState();
		},
		_setWidgetState: function() {
			var _widgetState = {
	    			page: 1,
	    			html: null,
	    			startIndex: 1,
	    			endIndex: 1,
	    			totalResults: 0,
	    			paginationFooter: "",
	    			highlightedIndex: 1,
	    			maximumNumberOfPages: 0,
	    			data: null,
	    			fetchedData: null
				};

			this.element.data({widgetState: _widgetState});
			this._constructPaginationFooter();
		},
		_getWidgetState: function() {
			return this.element.data("widgetState");
		},
	    _setOption: function( key, value ) {
	    	if(this._super) {
	    		this._super( key, value );
	    		return;
	    	}
	        $.Widget.prototype._setOption.apply( this, arguments );
	    },
	    _setOptions: function( options ) {
	    	if(this._super) {
		        this._super( options );
	    		return;
	    	}
	    	$.Widget.prototype._setOptions.apply( this, arguments );
	    },
	    _fireEvent: function(eventName, event, data) {
	    	if(eventName === this.itemSelectedEvent && !data) {
	    		data = this._getSelectedItemObject();
	    	}
	    	// re-set the widget state.
	    	this._resetWidgetState();
			this._trigger(eventName, event, data);
	    },
	    _getSelectedItemObject: function() {
	    	var data = {},
	    		widgetState = this._getWidgetState(),
	    		od = widgetState.fetchedData;

    		if(od && od.length > 0) {
    			data = od[widgetState.startIndex + widgetState.highlightedIndex - 1];
    		}
    		return data;
	    },
	    destroy: function() {
	    	this._cleanup();
	    	this.element.removeClass( this.widgetBaseClass ).unwrap();
	    }
	});
}(jQuery));
